/**
@file
Contains FromString functions.

This header file would undefine max and min macros.

Version:
0.1.0

Last updated date:
2012-12-11

Created date:
2012-08-15
*/


#pragma once
#include <string>
#include <type_traits>
#include "CString.h"


#ifdef max
#undef max
#endif

#ifdef min
#undef min
#endif
	

namespace Zaf {
namespace String {
namespace __Internal {


template<typename ValueType>
struct __IsLesserThanLLong {
	static const bool Value = true;
};

template<>
struct __IsLesserThanLLong<long long> {
	static const bool Value = false;
};

template<>
struct __IsLesserThanLLong<unsigned long long> {
	static const bool Value = false;
};

template<typename ValueType>
struct __MakeLargerType {
	typedef typename std::conditional<
		__IsLesserThanLLong<ValueType>::Value, 
		typedef typename std::conditional<
			std::is_signed<ValueType>::value,
			long,
			unsigned long
		>::type,
		typedef typename std::conditional<
			std::is_signed<ValueType>::value,
			long long,
			unsigned long long
		>::type
	>::type Type;
};



template<typename ValueType, typename CharType>
struct __StrToULongWrapper {
	static typename unsigned long __Func(const CharType* begin, CharType** end, int radix) {
		return StrToULong(begin, end, radix);
	}
};

template<typename ValueType, typename CharType>
struct __StrToULLongWrapper {
	static typename unsigned long long __Func(const CharType* begin, CharType** end, int radix) {
		return StrToULLong(begin, end, radix);
	}
};


template<typename ValueType, typename CharType>
struct __SelectConverter {
	typedef typename std::conditional<
		__IsLesserThanLLong<ValueType>::Value, 
		__StrToULongWrapper<ValueType, CharType>, 
		__StrToULLongWrapper<ValueType, CharType>
	>::type Type;
};



template<typename ValueType, typename CharType>
bool __S2N_Dec(const CharType* begin, CharType** end, int radix, ValueType& value) {

	bool hasNegativeSign = (*begin == '-');

	__MakeLargerType<ValueType>::Type tmpValue = 
		__SelectConverter<ValueType, CharType>::Type::__Func(begin, end, radix);

	if (errno != 0) {
		return false;
	}

	if ((hasNegativeSign == true && tmpValue > 0) ||
		(hasNegativeSign == false && tmpValue < 0)) {
			return false;
	}

	if (tmpValue < std::numeric_limits<ValueType>::min() || tmpValue > std::numeric_limits<ValueType>::max()) {
		return false;
	}

	value = (ValueType)tmpValue;
	return true;
}



template<typename ValueType, typename CharType>
bool __S2N_NonDec(const CharType* begin, CharType** end, int radix, ValueType& value) {

	std::make_unsigned<__MakeLargerType<ValueType>::Type>::type tmpValue = 
		__SelectConverter<ValueType, CharType>::Type::__Func(begin, end, radix);

	if (errno != 0) {
		return false;
	}

	if (tmpValue > std::numeric_limits<std::make_unsigned<ValueType>::type >::max()) {
		return false;
	}

	value = (ValueType)tmpValue;
	return true;
}



template<typename ValueType, typename CharType>
bool __S2N(const CharType* begin, CharType** end, int radix, ValueType& value) {

	errno = 0;

	if (radix == 10) {
		return __S2N_Dec(begin, end, radix, value);
	}
	else {
		return __S2N_NonDec(begin, end, radix, value);
	}
}



}  // __Internal



#define __Zaf_FromString_DefaultRadix__ (10)



/**
This is an overload version of %TryFromString, for c-style string.

@see TryFromString(const std::basic_string<CharType>&, ValueType&, int radix)
*/
template<typename ValueType, typename CharType>
bool TryFromString(const CharType* str, ValueType& value, int radix = __Zaf_FromString_DefaultRadix__) {

	//Check empty string
	if (str[0] == 0) {
		return false;
	}

	//Check blank chars
	if (IsSpace(str[0])) {
		return false;
	}

	//Check sign chars
	if (radix != 10 && (str[0] == '-' || str[0] == '+')) {
		return false;
	}

	CharType* end;
	ValueType tmpValue;
	if (__Internal::__S2N(str, &end, radix, tmpValue) == false) {
		return false;
	}

	//Check whether whole string has been converted.
	if (end[0] != 0) {
		return false;
	}

	value = tmpValue;

	return true;
}



/**
Tries to convert a string to integer value.

@param str %String to be converted.
@param value Reference to a variable to preserve the result.
@param radix The radix of number to be used, default to 10.
@return true if the conversion succeeded; false otherwise.

The available type of \a value is included in the list below:
- char
- signed char
- unsigned char
- wchar_t
- short
- unsigned short
- int
- unsigned int
- long
- unsigned long
- long long
- unsigned long long

\a str must fit the numeric format exactly. It means that \a str
cannot have any invalid character, even blank character, at its 
head or tail.

\a str cannot contain sign characters, if \a radix isn't 10.

Negative number cannnot be converted to unsigned types. For 
example, converting "-1" to unsigned int would failed.

Also, number cannot be converted to types that would cause 
overflow, such as converting "255" to signed char.

\a value wouldn't be changed if conversion failed.

\a radix must be in range 2 - 36, or it would cause undefined 
behavior.
*/
template<typename ValueType, typename CharType>
inline bool TryFromString(const std::basic_string<CharType>& str, ValueType& value, int radix = __Zaf_FromString_DefaultRadix__) {
	return TryFromString(str.c_str(), value, radix);
}



/**
This is an overload version of %FromString, for c-style string.

@see FromString(const std::basic_string<CharType>&, int radix)
*/
template<typename ValueType, typename CharType>
inline ValueType FromString(const CharType* str, int radix = __Zaf_FromString_DefaultRadix__) {
	ValueType value = ValueType();
	TryFromString(str, value, radix);
	return value;
}



/**
Converts a string to integer value.

@param str %String to be converted.
@param radix The radix of number to be used, default to 10.
@return Integer value, the conversion result.

This function is similar to %TryFromString, except that it
returns the result directly. If the conversion failed, it
returns 0. Therefore you cannot determine whether the conversion 
is succeeded or not.

@see TryFromString(const std::basic_string<CharType>&, ValueType&, int radix)
*/
template<typename ValueType, typename CharType>
inline ValueType FromString(const std::basic_string<CharType>& str, int radix = __Zaf_FromString_DefaultRadix__) {
	return FromString<ValueType>(str.c_str(), radix);
}


}  // String
}  // Zaf